OneToOneResolver.java
package org.codefilarete.stalactite.engine.configurer.resolver.onetoone;
import java.util.function.Consumer;
import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
import org.codefilarete.stalactite.dsl.MappingConfigurationException;
import org.codefilarete.stalactite.dsl.property.CascadeOptions;
import org.codefilarete.stalactite.engine.configurer.manyToOne.ManyToOneOwnedBySourceConfigurer.MandatoryRelationAssertBeforeUpdateListener;
import org.codefilarete.stalactite.engine.configurer.model.ResolvedOneToOneRelation;
import org.codefilarete.stalactite.engine.configurer.onetoone.OneToOneConfigurerTemplate.MandatoryRelationAssertBeforeInsertListener;
import org.codefilarete.stalactite.engine.configurer.resolver.SkeletonAggregateResolver;
import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
import org.codefilarete.stalactite.engine.runtime.onetoone.AbstractOneToOneEngine;
import org.codefilarete.stalactite.engine.runtime.onetoone.OneToOneOwnedBySourceEngine;
import org.codefilarete.stalactite.engine.runtime.onetoone.OneToOneOwnedByTargetEngine;
import org.codefilarete.stalactite.sql.ddl.structure.KeyMapping;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
public class OneToOneResolver {
private final SkeletonAggregateResolver skeletonAggregateResolver;
public OneToOneResolver(SkeletonAggregateResolver skeletonAggregateResolver) {
this.skeletonAggregateResolver = skeletonAggregateResolver;
}
/**
* Configure the given one-to-one relation by creating the persister for its target entity type and the engine that
* will maintain the cascades.
* It calls the given {@link Consumer} for the created target persister.
*
* @param relationDefinition the one-to-one object that defines the relation to append
* @param sourcePersister the persister having the one-to-one relation
* @param createdPersisterConsumer a consumer to handle the created persister
* @param <SRC> type of the source entity
* @param <SRCID> type of the source entity's identifier
* @param <TRGT> type of the target entity
* @param <TRGTID> type of the target entity's identifier
* @param <LEFTTABLE> type of the left table in the one-to-one relation
* @param <RIGHTTABLE> type of the right table in the one-to-one relation
* @param <JOINID> type of the join identifier
*/
public <SRC, SRCID, TRGT, TRGTID, LEFTTABLE extends Table<LEFTTABLE>, RIGHTTABLE extends Table<RIGHTTABLE>, JOINID>
void resolve(ResolvedOneToOneRelation<SRC, TRGT, LEFTTABLE, RIGHTTABLE, JOINID> relationDefinition,
ConfiguredRelationalPersister<SRC, SRCID> sourcePersister,
Consumer<ConfiguredRelationalPersister<TRGT, TRGTID>> createdPersisterConsumer) {
assertConfigurationIsSupported(relationDefinition.getRelationMode());
ConfiguredRelationalPersister<TRGT, TRGTID> targetPersister = skeletonAggregateResolver.buildPersister(relationDefinition.getTargetEntity());
createdPersisterConsumer.accept(targetPersister);
ReadWritePropertyAccessPoint<SRC, TRGT> targetAccessor = relationDefinition.getAccessor();
KeyMapping<LEFTTABLE, RIGHTTABLE, JOINID> foreignKeyColumnsMapping = relationDefinition.getJoin().getLeftKey().reference(relationDefinition.getJoin().getRightKey());
AbstractOneToOneEngine<SRC, TRGT, SRCID, TRGTID, LEFTTABLE, RIGHTTABLE> oneToOneEngine;
if (relationDefinition.isOwnedByTarget()) {
oneToOneEngine = new OneToOneOwnedByTargetEngine<>(sourcePersister, targetPersister, targetAccessor, foreignKeyColumnsMapping.getMapping());
} else {
oneToOneEngine = new OneToOneOwnedBySourceEngine<>(sourcePersister, targetPersister, targetAccessor, foreignKeyColumnsMapping.getMapping());
}
boolean orphanRemoval = relationDefinition.getRelationMode() == CascadeOptions.RelationMode.ALL_ORPHAN_REMOVAL;
boolean writeAuthorized = relationDefinition.getRelationMode() != CascadeOptions.RelationMode.READ_ONLY;
if (writeAuthorized) {
// if cascade is mandatory, then adding nullability checking before insert
if (relationDefinition.isMandatory()) {
sourcePersister.addInsertListener(new MandatoryRelationAssertBeforeInsertListener<>(targetAccessor));
sourcePersister.addUpdateListener(new MandatoryRelationAssertBeforeUpdateListener<>(targetAccessor));
}
oneToOneEngine.addInsertCascade();
oneToOneEngine.addUpdateCascade(orphanRemoval);
oneToOneEngine.addDeleteCascade(orphanRemoval);
} else {
// even if write is not authorized, we still have to insert and update source-to-target link, because we are in relation-owned-by-source
if (!relationDefinition.isOwnedByTarget()) {
((OneToOneOwnedBySourceEngine) oneToOneEngine).addForeignKeyMaintainer();
}
}
}
private void assertConfigurationIsSupported(CascadeOptions.RelationMode maintenanceMode) {
if (maintenanceMode == CascadeOptions.RelationMode.ASSOCIATION_ONLY) {
throw new MappingConfigurationException(CascadeOptions.RelationMode.ASSOCIATION_ONLY + " is only relevant for one-to-many association");
}
}
}